package owl2vowl.parser.vowl.property;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.annotation.Nonnull;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDatatype;
import org.semanticweb.owlapi.model.OWLObjectComplementOf;
import org.semanticweb.owlapi.model.OWLObjectIntersectionOf;
import org.semanticweb.owlapi.model.OWLObjectOneOf;
import org.semanticweb.owlapi.model.OWLObjectUnionOf;
import org.semanticweb.owlapi.model.OWLObjectVisitorEx;
import org.semanticweb.owlapi.model.OWLProperty;

import owl2vowl.model.data.VowlData;
import owl2vowl.model.entities.nodes.AbstractNode;
import owl2vowl.model.entities.nodes.classes.NullClass;
import owl2vowl.model.entities.nodes.classes.VowlClass;
import owl2vowl.model.entities.nodes.classes.VowlThing;
import owl2vowl.parser.owlapi.IndividualsVisitor;

public class DomainRangeVisitor implements OWLObjectVisitorEx<AbstractNode> {
	private final OWLProperty owlObjectProperty;
	private final VowlData vowlData;
	private Logger logger = LogManager.getLogger(DomainRangeVisitor.class);

	public DomainRangeVisitor(OWLProperty owlObjectProperty, VowlData vowlData) {
		this.owlObjectProperty = owlObjectProperty;
		this.vowlData = vowlData;
	}

	@Nonnull
	public AbstractNode doDefault(@Nonnull Object object) {
		this.logger.info("Missed object for domain/range: " + object);
		return (AbstractNode) object;
	}

	@Nonnull
	public AbstractNode visit(@Nonnull OWLObjectIntersectionOf ce) {
		Set operands = ce.getOperands();
		HashSet iriList = new HashSet();
		Iterator intersection = operands.iterator();

		while (intersection.hasNext()) {
			OWLClassExpression operand = (OWLClassExpression) intersection.next();
			if (operand.isAnonymous()) {
				this.logger.info("Anonymous operand in intersections");
				return new NullClass();
			}

			iriList.add(operand.asOWLClass().getIRI());
		}

		if (iriList.contains(VowlThing.GENERIC_THING_IRI)) {
			iriList.remove(VowlThing.GENERIC_THING_IRI);
		}

		VowlClass intersection1 = this.vowlData.getSearcher().getIntersection(iriList);
		if (intersection1 == null) {
			intersection1 = this.vowlData.getGenerator().generateIntersection(iriList);
		}

		return intersection1;
	}

	@Nonnull
	public AbstractNode visit(OWLObjectUnionOf ce) {
		Set operands = ce.getOperands();
		HashSet iriList = new HashSet();
		Iterator intersection = operands.iterator();

		while (intersection.hasNext()) {
			OWLClassExpression operand = (OWLClassExpression) intersection.next();
			if (operand.isAnonymous()) {
				this.logger.info("Anonymous operand in intersections");
				return new NullClass();
			}

			iriList.add(operand.asOWLClass().getIRI());
		}

		if (iriList.contains(VowlThing.GENERIC_THING_IRI)) {
			iriList.remove(VowlThing.GENERIC_THING_IRI);
		}

		VowlClass intersection1 = this.vowlData.getSearcher().getUnion(iriList);
		if (intersection1 == null) {
			intersection1 = this.vowlData.getGenerator().generateUnion(iriList);
		}

		return intersection1;
	}

	@Nonnull
	public AbstractNode visit(OWLObjectComplementOf ce) {
		OWLClassExpression baseClass = ce.getOperand();
		if (baseClass.isAnonymous()) {
			this.logger.info("Complement base is anonym:" + baseClass);
			return new NullClass();
		} else {
			return this.vowlData.getGenerator().generateComplement(baseClass.asOWLClass().getIRI());
		}
	}

	@Nonnull
	public AbstractNode visit(@Nonnull OWLDatatype node) {
		return this.vowlData.getGenerator().generateDatatypeReference(node.getIRI());
	}

	@Nonnull
	public AbstractNode visit(OWLObjectOneOf ce) {
		VowlClass oneOfClass = this.vowlData.getGenerator().generateAnonymousClass();
		ce.getIndividuals().forEach((owlIndividual) -> {
			owlIndividual
					.accept(new IndividualsVisitor(this.vowlData, this.vowlData.getOwlManager(), oneOfClass.getIri()));
		});
		return oneOfClass;
	}
}